package com.yifeng.repo.controller.traffic.apt;

import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Names;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Created by daibing on 2021/11/25.
 */
public class APT {
    private final TreeMaker treeMaker;
    private final Names names;

    public APT(TreeMaker treeMaker, Names names) {
        this.treeMaker = treeMaker;
        this.names = names;
    }

    /**
     * 获取方法参数名列表
     */
    public List<String> getArgNames(List<JCTree.JCVariableDecl> params) {
        ListBuffer<String> argNames = new ListBuffer<>();
        for (JCTree.JCVariableDecl param : params) {
            argNames.add(param.getName().toString());
        }
        return argNames.toList();
    }

    /**
     * 检查方法返回是否为空
     */
    public boolean returnVoid(JCTree.JCExpression restype) {
        return "void".equals(restype.toString());
    }

    /**
     * 检查方法注解是否包含目标注解
     */
    public boolean containAnnotation(List<JCTree.JCAnnotation> jcAnnotations, Class<?> targetAnnotation) {
        for (JCTree.JCAnnotation jcAnnotation : jcAnnotations) {
            if (jcAnnotation.getAnnotationType().toString().equals(targetAnnotation.getSimpleName())
                    || jcAnnotation.getAnnotationType().toString().equals(targetAnnotation.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据目标注解名称获取具体注解内容
     */
    public JCTree.JCAnnotation getAnnotation(List<JCTree.JCAnnotation> jcAnnotations, Class<?> targetAnnotation) {
        for (JCTree.JCAnnotation jcAnnotation : jcAnnotations) {
            if (jcAnnotation.getAnnotationType().toString().equals(targetAnnotation.getSimpleName())
                    || jcAnnotation.getAnnotationType().toString().equals(targetAnnotation.getName())) {
                return jcAnnotation;
            }
        }
        return null;
    }

    /**
     * 获取表达式的key和value
     * @param expression
     * @return
     */
    public Map<String, String> findKey2Value(JCTree.JCExpression expression) {
        Map<String, String> map = new HashMap<>(1);
        expression.accept(new TreeTranslator() {
            @Override
            public void visitAssign(JCTree.JCAssign jcAssign) {
                map.put(jcAssign.getVariable().toString(), jcAssign.getExpression().toString());
                super.visitAssign(jcAssign);
            }
        });
        return map;
    }

    /**
     * 对方法参数，直接设置字段名作为获取方法
     */
    public Map<String, String> findField2Method(List<JCTree.JCVariableDecl> params, List<String> fields) {
        Map<String, String> field2Method = new HashMap<>(fields.size());
        for (JCTree.JCVariableDecl param : params) {
            String key = param.getName().toString();
            if (!fields.contains(param.getName().toString())) {
                continue;
            }
            if (((Type.ClassType) param.sym.type).supertype_field.tsym.toString().equals(Enum.class.getName())) {
                field2Method.put(key, key + ".name");
            } else {
                field2Method.put(key, key);
            }
        }
        return field2Method;
    }

    /**
     * 对参数名和参数对象,查找特定字段对应的获取方法
     */
    public Map<String, String> findField2Method(String argName, JCTree.JCClassDecl argModel, List<String> fields) {
        Set<JCTree.JCMethodDecl> modelMethod = new HashSet<>();
        argModel.accept(new TreeTranslator() {
            @Override
            public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
                modelMethod.add(jcMethodDecl);
                super.visitMethodDef(jcMethodDecl);
            }
        });
        Map<String, String> field2Method = new HashMap<>(fields.size());
        for (JCTree.JCMethodDecl jcMethodDecl : modelMethod) {
            String mth = jcMethodDecl.getName().toString();
            if (!(mth.startsWith("get") || mth.startsWith("is"))) {
                continue;
            }
            for (String field : fields) {
                if (!(mth.equalsIgnoreCase("get" + field) || mth.equalsIgnoreCase("is" + field))) {
                    continue;
                }
                // 字段对应的获取方法带上参数名
                if (((Type.ClassType) jcMethodDecl.getReturnType().type).supertype_field.tsym.toString().equals(Enum.class.getName())) {
                    field2Method.put(field, argName + "." + mth + "." + "name");
                } else {
                    field2Method.put(field, argName + "." + mth);
                }
                break;
            }
        }
        return field2Method;
    }


    /**
     * 方法的多级访问
     * goodsStockDetail.getPlatform.name -> goodsStockDetail.getPlatform().name
     */
    public JCTree.JCExpression functionAccess(String components, int index) {
        String[] componentArray = components.split("\\.");
        JCTree.JCExpression expr = treeMaker.Ident(names.fromString(componentArray[0]));
        for (int i = 1; i < index; i++) {
            expr = treeMaker.Select(expr, names.fromString(componentArray[i]));
        }
        for (int i = index; i < componentArray.length; i++) {
            expr = treeMaker.Select(treeMaker.Apply(List.nil(), expr, List.nil()), names.fromString(componentArray[i]));
        }
        return expr;
    }

    /**
     * 创建 域/方法 的多级访问, 方法的标识只能是最后一个
     * goodsStockDetail.getPlatform.name -> goodsStockDetail.getPlatform.name
     */
    public JCTree.JCExpression memberAccess(String components) {
        String[] componentArray = components.split("\\.");
        JCTree.JCExpression expr = treeMaker.Ident(names.fromString(componentArray[0]));
        for (int i = 1; i < componentArray.length; i++) {
            expr = treeMaker.Select(expr, names.fromString(componentArray[i]));
        }
        return expr;
    }

    /**
     * 扩展支持泛型
     */
    public JCTree.JCExpression memberAccess(String components, String... typeList) {
        // 类
        JCTree.JCExpression expr = memberAccess(components);
        // 泛型参数
        ListBuffer<JCTree.JCExpression> exprList = new ListBuffer<>();
        for (String type : typeList) {
            exprList.add(memberAccess(type));
        }
        return treeMaker.TypeApply(expr, exprList.toList());
    }

    /**
     * 将入参对应的值作为文本内容
     * data.setClazz("EndpointServiceImpl");
     */
    public JCTree.JCStatement methodByValue(String instanceName, String methodName, Object... valueList) {
        ListBuffer<JCTree.JCExpression> exprList = new ListBuffer<>();
        for (Object value : valueList) {
            exprList.add(treeMaker.Literal(value));
        }
        return treeMaker.Exec(
                treeMaker.Apply(
                        List.nil(),
                        this.memberAccess(instanceName + "." + methodName),
                        exprList.toList()
                )
        );
    }

    /**
     * 将入参对应的值作为字段参数
     * data.setArgMap(argMap);
     */
    public JCTree.JCStatement methodByArg(String instanceName, String methodName, String... argList) {
        ListBuffer<JCTree.JCExpression> exprList = new ListBuffer<>();
        for (String arg : argList) {
            exprList.add(treeMaker.Ident(names.fromString(arg)));
        }
        return treeMaker.Exec(
                treeMaker.Apply(
                        List.nil(),
                        this.memberAccess(instanceName + "." + methodName),
                        exprList.toList()
                )
        );
    }

    /**
     * 将入参对应的值作为文本内容+字段参数
     * argMap.put("batchNo", batchNo);
     */
    public JCTree.JCStatement methodByMap(String instanceName, String methodName, Object value, String arg) {
        ListBuffer<JCTree.JCExpression> exprList = new ListBuffer<>();
        exprList.add(treeMaker.Literal(value));
        exprList.add(treeMaker.Ident(names.fromString(arg)));
        return treeMaker.Exec(
                treeMaker.Apply(
                        List.nil(),
                        this.memberAccess(instanceName + "." + methodName),
                        exprList.toList()
                )
        );
    }
}
